package com.lightsaber.trade.as.service.planning;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.lightsaber.trade.as.dao.category.CategoryLeafDao;
import com.lightsaber.trade.as.dao.category.CategoryNodeDao;
import com.lightsaber.trade.as.dao.planning.CategoryPlanningItemDao;
import com.lightsaber.trade.as.dao.planning.Level1PlanningDao;
import com.lightsaber.trade.as.entity.category.Category;
import com.lightsaber.trade.as.entity.category.CategoryLeaf;
import com.lightsaber.trade.as.entity.category.CategoryNode;
import com.lightsaber.trade.as.entity.planning.CategoryPlanningItem;
import com.lightsaber.trade.as.entity.planning.Level1Planning;
import com.lightsaber.trade.as.entity.planning.Planning;

@Service
@Transactional
public class PreviewEndorsedManager {
    private final static Logger LOG = LoggerFactory.getLogger(PreviewEndorsedManager.class);

    @Autowired
    private PlanningService planningService;
    @Autowired
    private Level1PlanningDao level1PlanningDao;

    @Autowired
    private CategoryPlanningItemDao categoryPlanningItemDao;

    @Autowired
    private CategoryLeafDao categoryLeafDao;

    @Autowired
    private CategoryNodeDao categoryNodeDao;

    private static final String FACTOR = "FACTOR";
    private static final String HOURS = "HOURS";
    private static final String PLANNED = "PLANNED";
    private static final String ACTUAL = "ACTUAL";

    public String preview(final Planning planning) {

        StringBuffer gridDoc = new StringBuffer();
        gridDoc.append("<Grid><Cfg id=\"PreviewGrid\" MainCol=\"A\" ColMoving=\"0\" Dragging=\"0\" Sorting=\"0\" Alternate=\"0\"/>");

        Category masterTree = planning.getCategory();

        // 1. Add columns to Grid
        generateLeftCols(gridDoc, masterTree);

        // 2. Add columns to Grid
        generateCols(gridDoc, masterTree);

        // 3. Add Def to Grid
        generateDef(gridDoc, masterTree);

        // 4. Add Head to Grid
        gridDoc.append("<Head>");
        generateHead1(gridDoc, masterTree);
        generateHead2(gridDoc, masterTree);
        gridDoc.append("</Head>");

        // 5. Add body to Grid
        generateBody(gridDoc, masterTree);

        gridDoc.append("<Foot><I Def='Tree' A='&lt;B&gt;Total&lt;/B&gt;' AType='Html' CanEdit='0' /></Foot>");
        gridDoc.append("<Toolbar Visible=\"0\"/>");
        gridDoc.append("<Panel Copy=\"0\" Move=\"0\" Select=\"0\" Delete=\"0\"/></Grid>");
        return gridDoc.toString();
    }

    private void generateHead1(final StringBuffer gridDoc, final Category category) {
        StringBuffer sb = new StringBuffer();
        Integer planYear = category.getPlanYear();

        sb.append("<Header Spanned=\"1\" ASpan=\"6\" A=\"Planning Exercise : " + planYear + "/"
                + (planYear + 1) + "\" AType=\"Html\" ");

        Integer level1Start = category.getLevel1Start();
        Integer level1End = category.getLevel1End();

        for (Integer i = level1Start; i <= level1End; i++) {
            String prefix = "_" + i + "_" + (i + 1) + "_";

            if (i >= planYear) {
                sb.append(prefix + FACTOR + "Span=\"3\" " + prefix + FACTOR + "=\"" + i + "-"
                        + (i + 1) + "\" " + prefix + FACTOR + "Align=\"Center\" " + prefix + FACTOR
                        + "Type=\"Html\"  ");

            } else {

                sb.append(prefix + FACTOR + "Span=\"4\" " + prefix + FACTOR + "=\"" + i + "-"
                        + (i + 1) + "\" " + prefix + FACTOR + "Align=\"Center\" " + prefix + FACTOR
                        + "Type=\"Html\"  ");
            }

        }

        sb.append("/>");
        LOG.debug("Head1 ={}", sb.toString());
        gridDoc.append(sb.toString());
    }

    private void generateHead2(final StringBuffer gridDoc, final Category category) {
        StringBuffer sb = new StringBuffer();

        sb.append("<Header id=\"Header\" ");
        sb.append("A=\"Programme\" AAlign=\"Left\" AType=\"Html\" ");
        sb.append("C=\"Funding&lt;br/&gt;Mode\" CAlign=\"Center\" CType=\"Html\" ");
        sb.append("VButton=\"Discipline&lt;br/&gt;View\" VButtonAlign=\"Center\" VButtonType=\"Html\" ");
        sb.append("SLabel=\"Status\" SLabelAlign=\"Center\" SLabelType=\"Html\" ");
        sb.append("RCheck=\"Reject\" RCheckAlign=\"Center\" RCheckType=\"Html\" ");
        sb.append("RInput=\"Reason\" RInputAlign=\"Left\" RInputType=\"Html\" ");

        Integer level1Start = category.getLevel1Start();
        Integer level1End = category.getLevel1End();
        Integer planYear = category.getPlanYear();

        for (Integer i = level1Start; i <= level1End; i++) {
            String prefix = "_" + i + "_" + (i + 1) + "_";

            if (i >= planYear) {
                sb.append(prefix + FACTOR + "=\"Conversion&lt;br/&gt;Factor\" " + prefix + FACTOR
                        + "Align=\"Center\" " + prefix + FACTOR + "Type=\"Html\" ");

                sb.append(prefix + PLANNED + "=\"Planned&lt;br/&gt;Places\" " + prefix + PLANNED
                        + "Align=\"Center\" " + prefix + PLANNED + "Type=\"Html\" ");

                sb.append(prefix + HOURS + "=\"Planned&lt;br/&gt;Training&lt;br/&gt;Hours\" "
                        + prefix + HOURS + "Align=\"Center\" " + prefix + HOURS + "Type=\"Html\" ");

            } else {
                sb.append(prefix + FACTOR + "=\"Conversion&lt;br/&gt;Factor\" " + prefix + FACTOR
                        + "Align=\"Center\" " + prefix + FACTOR + "Type=\"Html\" ");

                sb.append(prefix + PLANNED + "=\"Planned&lt;br/&gt;Places\" " + prefix + PLANNED
                        + "Align=\"Center\" " + prefix + PLANNED + "Type=\"Html\" ");

                sb.append(prefix + HOURS + "=\"Planned&lt;br/&gt;Training&lt;br/&gt;Hours\" "
                        + prefix + HOURS + "Align=\"Center\" " + prefix + HOURS + "Type=\"Html\" ");

                sb.append(prefix + ACTUAL + "=\"Actual&lt;br/&gt;Enrolment\" " + prefix + ACTUAL
                        + "Align=\"Center\" " + prefix + ACTUAL + "Type=\"Html\" ");

            }

        }

        sb.append("/>");
        LOG.debug("Head2 ={}", sb.toString());
        gridDoc.append(sb.toString());
    }

    private void generateDef(final StringBuffer gridDoc, final Category category) {
        StringBuffer sb = new StringBuffer();

        sb.append("<Def>");
        sb.append("<D Name=\"Tree\" Calculated=\"1\" ");

        Integer level1Start = category.getLevel1Start();
        Integer level1End = category.getLevel1End();
        Integer planYear = category.getPlanYear();

        for (Integer i = level1Start; i <= level1End; i++) {
            String prefix = "_" + i + "_" + (i + 1) + "_";

            if (i >= planYear) {
                sb.append(prefix + HOURS + "Formula=\"sum()\" ");
                sb.append(prefix + PLANNED + "Formula=\"sum()\" ");

            } else {
                sb.append(prefix + HOURS + "Formula=\"sum()\" ");
                sb.append(prefix + PLANNED + "Formula=\"sum()\" ");
                sb.append(prefix + ACTUAL + "Formula=\"sum()\" ");

            }

        }

        sb.append("/>");
        sb.append("</Def>");

        LOG.debug("Def ={}", sb.toString());
        gridDoc.append(sb.toString());
    }

    private void generateBody(final StringBuffer gridDoc, final Category masterTree) {
        StringBuffer sb = new StringBuffer();
        Level1Planning level1Planning = level1PlanningDao.findByBranchTree(masterTree);

        sb.append("<Body><B>");

        recursionMasterTree(sb, masterTree.getRoot(), level1Planning);

        sb.append("</B></Body>");

        LOG.debug("body ={}", sb.toString());
        gridDoc.append(sb.toString());
    }

    private void recursionMasterTree(final StringBuffer sb, final CategoryNode node,
            final Level1Planning level1Planning) {

        // Receive branch tree
        if (node.getOwnedBranchTrees().size() > 0) {

            sb.append("<I Def=\"Tree\" A=\""
                    + node.getName()
                    + "\" SLabel=\"Endorsed\" RCheck=\"0\" RCheckCanEdit=\"1\" RInputCanEdit=\"1\" >");

            for (CategoryNode child : node.getChildren()) {
                recursionEndorsedBranchTree(sb, child, level1Planning);
            }

            sb.append("</I>");
            return;
        }

        // ---------------------- Finally ----------------------START
        // Receive bottom
        if (node.getChildren().size() <= 0) {
            sb.append("<I Def=\"Leaf\" A=\"" + node.getName() + "\" CanEdit=\"0\" ></I>");
            return;
        }

        // Not Receive bottom
        sb.append("<I Def=\"Tree\" A=\"" + node.getName() + "\" >");
        for (CategoryNode child : node.getChildren()) {
            recursionMasterTree(sb, child, level1Planning);
        }

        sb.append("</I>");
        // ---------------------- Finally ----------------------END
    }

    private void recursionEndorsedBranchTree(final StringBuffer sb, final CategoryNode node,
            final Level1Planning level1Planning) {

        // ---- Discriminate the view discipline button will display -----START
        String button = "";
        Integer isDisciplineView = node.getIsDisciplineView();
        if (isDisciplineView != null && isDisciplineView == 1) {
            button = "VButton='&lt;input type=&quot;button&quot; class=&quot;dButton&quot; onclick=&quot;toDisView(&apos;"
                    + level1Planning.getId()
                    + "&apos;,&apos;"
                    + node.getId()
                    + "&apos;)&quot; value=&quot;View&quot; /&gt;' ";
        }
        // ---- Discriminate the view discipline button will display -----END

        if (node.getIsLeafNode() == 1) {
            Category branchTree = level1Planning.getCategory();
            CategoryLeaf categoryLeaf = categoryLeafDao.get(node.getId());

            Integer level1Start = branchTree.getLevel1Start();
            Integer level1End = branchTree.getLevel1End();
            Integer planYear = branchTree.getPlanYear();

            sb.append("<I Def=\"Leaf\" A=\"" + node.getName() + "\"  C=\""
                    + categoryLeaf.getFundingMode() + "\" " + button + " ");

            for (Integer i = level1Start; i <= level1End; i++) {
                String prefix = "_" + i + "_" + (i + 1) + "_";

                CategoryPlanningItem categoryPlanningItem = categoryPlanningItemDao
                        .findByPlanAndLeafAndYear(level1Planning, categoryLeaf, i);

                if (i >= planYear) {

                    sb.append(prefix + FACTOR + "=\"" + categoryPlanningItem.getConvertRate()
                            + "\" ");
                    sb.append(prefix + PLANNED + "=\"" + categoryPlanningItem.getPlannedPlaces()
                            + "\" ");
                    sb.append(prefix + HOURS + "=\""
                            + categoryPlanningItem.getPlanedTrainingHours() + "\" ");

                } else {

                    sb.append(prefix + FACTOR + "=\"" + categoryPlanningItem.getConvertRate()
                            + "\" ");
                    sb.append(prefix + PLANNED + "=\"" + categoryPlanningItem.getPlannedPlaces()
                            + "\" ");
                    sb.append(prefix + HOURS + "=\""
                            + categoryPlanningItem.getPlanedTrainingHours() + "\" ");
                    sb.append(prefix + ACTUAL + "=\"" + categoryPlanningItem.getActualEnrolment()
                            + "\" ");
                }
            }

            sb.append("></I>");

            return;
        }

        // Receive bottom
        if (node.getChildren().size() <= 0) {
            sb.append("<I Def=\"Leaf\" A=\"" + node.getName() + "\" CanEdit=\"0\" ></I>");
            return;
        }

        // Not Receive bottom
        sb.append("<I Def=\"Tree\" A=\"" + node.getName() + "\" " + button + " >");
        for (CategoryNode child : node.getChildren()) {
            recursionEndorsedBranchTree(sb, child, level1Planning);
        }
        sb.append("</I>");
    }

    private void generateLeftCols(final StringBuffer gridDoc, final Category category) {
        StringBuffer sb = new StringBuffer();

        sb.append("<LeftCols>");
        sb.append("<C Name=\"A\" Width=\"190\" CanEdit=\"0\" />");
        sb.append("<C Name=\"C\" Width=\"60\" CanEdit=\"0\" Align=\"Center\" />");
        sb.append("<C Name=\"VButton\" Width=\"65\" CanEdit=\"0\" Align=\"Center\" Type=\"Html\" />");
        sb.append("<C Name=\"SLabel\" Width=\"120\" CanEdit=\"0\" Align=\"Center\" Type=\"Html\" />");
        sb.append("<C Name=\"RCheck\" Width=\"65\" CanEdit=\"0\" Align=\"Center\" Type=\"Bool\" />");
        sb.append("<C Name=\"RInput\" Width=\"200\" CanEdit=\"0\" Align=\"Left\" Type=\"Lines\" />");
        sb.append("</LeftCols>");

        LOG.debug("LeftCols ={}", sb.toString());
        gridDoc.append(sb.toString());
    }

    private void generateCols(final StringBuffer gridDoc, final Category category) {
        StringBuilder sb = new StringBuilder();

        Integer level1Start = category.getLevel1Start();
        Integer level1End = category.getLevel1End();
        Integer planYear = category.getPlanYear();

        sb.append("<Cols>");

        for (Integer i = level1Start; i <= level1End; i++) {
            String prefix = "_" + i + "_" + (i + 1) + "_";

            if (i >= planYear) {
                sb.append("<C Name=\"" + prefix + FACTOR
                        + "\" Width=\"90\" Type=\"Text\" Align=\"Center\" CanEdit=\"0\"/>");

                sb.append("<C Name=\"" + prefix + PLANNED
                        + "\" Width=\"90\" Type=\"Float\" Align=\"Center\" CanEdit=\"0\"/>");

                sb.append("<C Name=\"" + prefix + HOURS
                        + "\" Width=\"90\" Type=\"Float\" Align=\"Center\" CanEdit=\"0\"/>");

            } else {
                sb.append("<C Name=\"" + prefix + FACTOR
                        + "\" Width=\"90\" Type=\"Text\" Align=\"Center\" CanEdit=\"0\"/>");

                sb.append("<C Name=\"" + prefix + PLANNED
                        + "\" Width=\"90\" Type=\"Float\" Align=\"Center\" CanEdit=\"0\"/>");

                sb.append("<C Name=\"" + prefix + HOURS
                        + "\" Width=\"90\" Type=\"Float\" Align=\"Center\" CanEdit=\"0\"/>");

                sb.append("<C Name=\"" + prefix + ACTUAL
                        + "\" Width=\"90\" Type=\"Float\" Align=\"Center\" CanEdit=\"0\"/>");

            }

        }

        sb.append("</Cols>");

        LOG.debug("Cols ={}", sb.toString());
        gridDoc.append(sb.toString());

    }

    public void endorse(final String planningId, final Map<String, String> endorseMap) {

        Map<CategoryNode, Level1Planning> selectedPlannings = new HashMap<CategoryNode, Level1Planning>();
        Set<Map.Entry<String, String>> entrySet = endorseMap.entrySet();

        CategoryNode ownerNode = null;
        Level1Planning level1Planning = null;

        for (Map.Entry<String, String> entry : entrySet) {

            ownerNode = categoryNodeDao.get(entry.getKey());

            String level1PlanningName = entry.getValue();
            level1Planning = level1PlanningDao.findByNameAndOwerNode(level1PlanningName, ownerNode);

            selectedPlannings.put(ownerNode, level1Planning);
        }

        Planning planning = planningService.get(planningId);

        // TODO change the name and remark
        String name = planning.getCategory().getPlanningExerciseName();
        String remarks = name + " remark";
        planningService.endorseFirstLevelPlanning(planning.getCategory(), selectedPlannings, name,
                remarks);
    }
}
